	TITLE	'Bad Sectors Scan Utility   6/May/1984  '
	.SALL
;---------------------------------------------------------------;
;								;
;	CP/M plus  Bad Sector Scan Utility			;
;								;
;	by Tetsuya Okada, Copyright (C) 1984			;
;								;
;	Version 1.00    6/May/1984				;
;								;
;---------------------------------------------------------------;
	$INCLUDE STDINCL.LIB
;
	.REQUEST STDLIB
	EXTRN	DECPRI
; 
	CSEG		; PROGRAM CODE BEGIN
	DEFB	'Copyright (C) 1984 by Tetsuya Okada',EOF
SCAN::
	LD	SP,STACK
RESTART:
	@MSG	<CR,LF,'SCAN - Bad Sectors Scan Utility for CP/M plus',CR,LF,CR,LF>
; 	
;	CP/M VERSION CHECK
;
	@BDOS	B$VERS	;RETURN VERSION NUMBER
	LD	A,H
	AND	A
	JR	NZ,VER$BAD	;NOT CP/M
	LD	A,L
	AND	0F0H		;IGNORE RELEASE NUMBER
	CP	30H
	JR	Z,VER$OK
VER$BAD:
	@MSG	<'ERROR: CP/M 3.X required.',CR,LF>
	@STOP
VER$OK:
;
;	PROMPT DRIVE NUMBER
;
MAIN$LOOP:
	@MSG	<'Enter Drive (A-O, or CR to end): '>
	@BDOS	B$CIN
	CP	CR
	JP	Z,STOP
	CP	'a'	;SMALL ?
	JR	C,NOT$SMALL
	SUB	'a'-'A'
NOT$SMALL:
	LD	(DRIVE),A
	@MSG	<CR,LF>
	LD	A,(DRIVE)
	CP	'A'
	JR	C,DR$ERROR
	CP	'O'+1
	JR	C,DR$OK
DR$ERROR:
	@MSG	<'ERROR: Invalid Drive',CR,LF>
	JP	MAIN$LOOP
DR$OK:
;
;	GET DPB
;
	LD	E,0FFH		;RETURN ERROR MODE
	@BDOS	B$SETER		;SET BDOS ERROR MODE
;	
	LD	HL,1		;MAKE DRIVE VECTOR
	LD	A,(DRIVE)
	SUB	'A'
SH$DVEC:			;SHIFT REG(A) TIMES
	DEC	A
	JR	NZ,RST$DRV
	ADD	HL,HL
	JR	SH$DVEC
RST$DRV:
	EX	DE,HL
	@BDOS	B$RSTDRV	;RESET DRIVE
;
	LD	A,(DRIVE)
	SUB	'A'
	LD	E,A	
	@BDOS	B$SELDSK	;SELECT DISK
	INC	A
	JP	NZ,SEL$SUC
	LD	A,H
	CP	4
	JP	Z,DR$ERROR
	JP	IO$ERROR
SEL$SUC:
	@BDOS	B$GETDPB	;GET DPB ADDRESS OF SELECTED DRIVE
	LD	(DPBADR),HL
	LD	A,H
	AND	L
	CP	0FFH
	JP	Z,IO$ERROR
;
;	CALC NUMBER OF PHISICAL SECTOR PER TRACK
;
	LD	IX,(DPBADR)
	LD	L,(IX+0)	;SPT (128 SECTORS PER TRACK)
	LD	H,(IX+1)
	LD	A,(IX+15)	;PSH (PHISICAL RECORD SHIFT FACTOR)
PS$SHIFT:
	AND	A
	JR	Z,PS$S1
	SRL	H		;HALVE HL 
	RR	L
	JP	C,DPBERROR	;ERROR IF REMAINDER IS NOT ZERO
	DEC	A
	JR	PS$SHIFT
PS$S1:
	LD	(PSPT),HL	;STORE PHISICAL SECTOR PER TRACK
;
;	CALC NUMBER OF TRACKS 
;
	LD	L,(IX+5)	;DSM (NUMBER OF BLOCKS - 1)
	LD	H,(IX+6)
	INC	HL
	LD	B,(IX+2)	;BSH (BLOCK SHIFT FACTOR)
TR$SHIFT:
	ADD	HL,HL
	JP	C,BIG$DISK
	DJNZ	TR$SHIFT
 	LD	E,(IX+0)	;SPT
	LD	D,(IX+1)
	LD	BC,1
TR$DIV:
	AND	A	;CLEAR CARRY FLAG
	SBC	HL,DE
	JR	C,TR$D1
	JR	Z,TR$D1
	INC	BC
	JR	TR$DIV
TR$D1:
	LD	(TRACKS),BC
;
	@MSG	<'Do you want to scan reserved tracks ? (y/n) '>
	CALL	YESNO
	JR	Z,RSV$SCAN
	LD	L,(IX+13)	;OFF (NUMBER OF RESERVED TRACKS)
	LD	H,(IX+14)
	LD	(CURTRK),HL
	JR	TR$E
RSV$SCAN:
	LD	HL,(TRACKS)
	LD	E,(IX+13)	;OFF
	LD	D,(IX+14)
	ADD	HL,DE
	LD	(TRACKS),HL
	LD	HL,0
	LD	(CURTRK),HL
TR$E:
	@MSG	<'Do you need sector skewing ? (y/n) '>
	CALL	YESNO
	LD	A,0
	JR	NZ,NO$SKEW
	LD	A,0FFH
NO$SKEW:
	LD	(SKEWF),A
;
;	SCAN DISK
;
@BIOS	MACRO	?FUNC		;;MACRO TO CALL BIOS
	CALL	BIOSSUB
	LD	A,?FUNC
	LD	(FUNC),A
	@BDOS	B$BIOS
	ENDM
;
	XOR	A
	LD	(ERROR),A	;CLEAR ERROR FLAG
;
	LD	HL,(TRACKS)
	LD	(TRKCNT),HL
NEXT$TRK:
	@MSG	<'Scanning Track - '>
	LD	HL,(CURTRK)
	CALL	DECPRI
	@MSG	<'      ',CR>
;
	LD	HL,1
	LD	(CURSEC),HL
NEXT$SEC:
	LD	BC,(CURTRK)
	@BIOS	10		;SETTRK
	LD	BC,(CURSEC)
	@BIOS	11		;SETSEC
	LD	BC,SECBUF
	@BIOS	12		;SETDMA
	@BIOS	13		;READ
	AND	A
	CALL	NZ,BAD$SEC
;
	LD	HL,(CURSEC)
	INC	HL
	LD	A,(SKEWF)
	AND	A
	JR	Z,NO$SK
	INC	HL
	LD	(CURSEC),HL
	LD	DE,(PSPT)
	SCF
	SBC	HL,DE
	JR	C,NEXT$SEC
	BIT	0,L
	JR	NZ,INC$TRK
	LD	HL,2
	LD	(CURSEC),HL
	JR	NEXT$SEC
NO$SK:
	LD	(CURSEC),HL
	LD	DE,(PSPT)
	SCF
	SBC	HL,DE
	JR	C,NEXT$SEC
INC$TRK:
	LD	HL,(CURTRK)
	INC	HL
	LD	(CURTRK),HL
	LD	HL,(TRKCNT)
	DEC	HL
	LD	(TRKCNT),HL
	LD	A,H
	OR	L
	JP	NZ,NEXT$TRK
;
;	SCANNING COMPLETE
;
	@MSG	<'Scanning Complete.    '>
	LD	A,(ERROR)
	AND	A
	JR	Z,NO$BAD
	LD	H,0
	LD	L,A
	CALL	DECPRI
	JR	BSMSG
NO$BAD:
	@MSG	<'No'>
BSMSG:
	@MSG	<' Bad Sectors.       ',CR,LF>
	JP	RESTART
;
STOP:
	@STOP
;
;	FATAL ERROR PROCESSING
;
IO$ERROR:
	@MSG	<'ERROR: Phisical Disk I/O',CR,LF>
	JP	RESTART
;
DPBERROR:
	@MSG	<'ERROR: Invalid Disk Parameter Block',CR,LF>
	JP	RESTART
;
BIG$DISK:
	@MSG	<'ERROR: This program cannot support such a big drive.',CR,LF>
	JP	RESTART
;
MED$CHG:
	@MSG	<'ERROR: Media density has changed.',CR,LF>
	JP	RESTART
;
;	SUBROUTINES
;
YESNO:		; FLAG Z: YES; NZ: NO
	@BDOS	B$CIN
	PUSH	AF
	@MSG	<CR,LF>
	POP	AF
	CP	'Y'
	RET	Z
	CP	'y'
	RET	
;
BIOSSUB:	; SET REGISTERS TO BIOSPB
	LD	(AREG),A
	LD	(BCREG),BC
	LD	(DEREG),DE
	LD	(HLREG),HL
	LD	DE,BIOSPB
	RET
;
BAD$SEC:
	POP	HL	;RETURN ADDRESS
	INC	A
	JP	Z,MED$CHG
	PUSH	HL	;RE-PUSH
	@MSG	<CR,'*** Bad Sector ***  Track - '>
	LD	HL,(CURTRK)
	CALL	DECPRI
	@MSG	<'   Sector - '>
	LD	HL,(CURSEC)
	CALL	DECPRI
	@MSG	<'            ',CR,LF>
;
	LD	HL,ERROR
	INC	(HL)
	RET	NZ
	DEC	(HL)
	RET
;
;	DATA AREA
;
	DSEG
;
DRIVE:	DEFS	1	;DRIVE SPEC. IN ASCII
DPBADR: DEFS	2	;DPB ADDRESS
PSPT:	DEFS 	2	;NUMBER OF PHISICAL SECTORS PER TRACK
TRACKS: DEFS	2	;NUMBER OF TRACK 
TRKCNT: DEFS	2	;TRACK COUNTER
CURTRK: DEFS	2	;CURRENT TRACK
CURSEC: DEFS	2	;CURRENT SECTOR
ERROR:	DEFS	1	;ERROR COUNT
SKEWF:	DEFS	1	;NONZERO IF SKEWING
;
BIOSPB:		;BIOS PARAMETER BLOCK FOR DIRECT BIOS CALL
FUNC:	DEFS	1
AREG:	DEFS	1
BCREG:	DEFS	2
DEREG:	DEFS	2
HLREG:	DEFS	2
;
	DEFS	32	;STACK ROOM
STACK:
;
SECBUF::		;MUST BE LAST OF .COM FILE
;	DEFS	1024
;
	END	SCAN
